#-*- coding:utf-8 -*-
import threading
import logging
import struct
import string

from serial import *

logging.basicConfig(level=logging.WARNING)

#===============================================================================
# class Packet:
#    peices = []
# 
#    def __init__(self):
#        pass
#    
#    def addPeice(self, name, data):
#        self.peices.append((name, data))
#        
#    def getPeice(self, name):
#        for n, d in self.peices:
#            if n == name:
#                return d
#        
#        return None
#    
#    def getEntireData(self):
#        data = []
#        for n, d in self.peices:
#            data.extend(d)
#            
#        return data
#    
#    def getLength(self):
#        return len(self.getEntireData())
#===============================================================================
    

LK_RX_METHOD_AC7 = 0x01
LK_RX_METHOD_AC14 = 0x02
LK_RX_METHOD_INV = 0x04

class Linker(object):
    listeners = []
    method = 0x00
    serial = None
    rxdata = []
    def __init__(self, method = LK_RX_METHOD_AC14):
        self.method = method
        
    
    def unpack(self, data):
        updata = None
        if self.method & LK_RX_METHOD_AC14:
            updata = self.unpackAC14(data)
        if updata is None and self.method & LK_RX_METHOD_AC7:
            updata = self.unpackAC7(data)
        if updata is None and self.method & LK_RX_METHOD_INV:
            updata = self.unpackInverter(data)
        
        return updata 
    
    def unpackAC7(self, data):
        n = 0
        while len(data[n:]) >= 7:
            peice = data[n:][:7]
            if peice[0] == 0x32 and peice[6] == 0x34 and peice[5] == self.xorchecksum(peice[1:5]):
                del data[:n]
                del data[:7]
                return peice[1:5]
            n = n + 1
            
        return None
        
    def unpackAC14(self, data):
        n = 0
        while len(data[n:]) >= 14:
            peice = data[n:][:14]
            if peice[0] == 0x32 and peice[13] == 0x34 and peice[12] == self.xorchecksum(peice[1:12]):
                del data[:n]
                del data[:14]
                return peice[1:12]
            n = n + 1
            
        return None
            
    def unpackInverter(self, data):
        return None
    
    def packAC(self, data):
        peice = [0x32] + data
        peice = peice + [self.xorchecksum(peice[1:]), 0x34]
        return peice
    
    def packInverter(self, data):
        return None
    
    def xorchecksum(self, data):
        ck = 0
        for d in data:
            ck = ck ^ d
        return ck
    
    def connect(self, sserial):
        if self.serial is None:
            self.serial = sserial
            self.serial.addListener(self.addRecvData)
        
    def disconnect(self):
        if self.serial is not None:
            self.serial.removeListener(self.addRecvData)
            self.serial = None
        
    def addRecvData(self, data):
        declst = [struct.unpack('B', c)[0] for c in list(data)]
        self.rxdata.extend(declst)
        data = self.unpack(self.rxdata)
        
        if data:
            for li in self.listeners:
                li(data)
            
       
    def sendAC(self, data):
        packet = self.packAC(data)
        enclst = [struct.pack('B', b) for b in packet]
        str = ''
        str = str.join(enclst)
        logging.debug("Send AC Packet : %s" % str)
        self.serial.write(str + "\n")
    
        


    def addListener(self, listener):
        self.listeners.append(listener)

    def removeListener(self, listener):
        self.listeners.remove(listener)
        
        
class SharableSerial(Serial):
    """Serial that can be used one or more other module.
     
    """
    def __init__(self, port="COM1", baudrate=9600):
        """Constructor. """
        Serial.__init__(self)
        self.event = threading.Event()
        self.listeners = []

        self.baudrate = baudrate
        self.port = port
        self.parity = PARITY_EVEN
        self.stopbits = STOPBITS_ONE
        self.bytesize = EIGHTBITS   
        self.timeout = 1          # 1 s      
        self.thread = None

    
    def addListener(self, listener):
        self.listeners.append(listener)


    def removeListener(self, listener):
        self.listeners.remove(listener)

        
    def open(self):
        if self.thread is None:
            super(SharableSerial, self).open()
            Serial.flush(self)
            self.event.set()
    
            self.thread = threading.Thread(target=self.serialThreadMain)
#            self.thread.setDaemon(1)
            self.event.set()
            self.thread.start()

                        
    def close(self):
        if self.thread is not None:
            self.event.clear()          #clear alive event for thread
            while self.thread.isAlive():
                self.thread.join(3)          #wait until thread has finished
                if self.thread.isAlive():    
                    logging.critical("Fail thread stop")
            self.thread = None
            Serial.close(self)

            
    def write(self, data):
        Serial.write(self, data)
            
            
    def serialThreadMain(self):
        """Thread that handles the incomming traffic. Does the basic input
           transformation (newlines) and generates an SerialRxEvent"""
        while self.event.isSet():               #loop while alive event is true
            logging.debug("Thread Alive")
            text = Serial.read(self, 1)          #read one, with timout
            if text:                            #check if not timeout
                logging.debug("Serial Rx")
                n = Serial.inWaiting(self)     #look if there is more to read
                if n:
                    text = text + Serial.read(self, n) #get it
                #newline transformation
                    
                logging.debug("Rx Handling")
                for mt in self.listeners:
                    logging.debug("Listener ID %d" % id(mt))
                    mt(text)
                logging.debug("Stop Rx Handling")
                

        
        